/*
 * @Author: secrets123
 * @Date: 2022-07-21 10:53:28
 * @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @LastEditTime: 2023-09-14 14:02:36
 * @FilePath: \stm32g431_AllRings\applications\iap_para.c
 * @Description:
 *
 * Copyright (c) 2022 by secrets123 1097290967@qq.com, All Rights Reserved.
 */
#include <rtthread.h>
#include <rtconfig.h>
#include <board.h>
#include "iap_para.h"
#include "allrings_sys.h"
#include "eeprom.h"
#include "soft_iic.h"

#define DBG_TAG "iap_para"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>

/*thread*/
#define THREAD_STACK_PARA_SAVE 1024
#define THREAD_PRIO_PARA_SAVE 18
static rt_thread_t tpara_save = RT_NULL;
rt_sem_t sem_saved = RT_NULL;

// #define USE_CRC_CHECK
#ifdef USE_CRC_CHECK
const uint8_t chCRCHTable[] = // CRC 高位字节值表
    {
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x00, 0xC1, 0x81, 0x40};
const uint8_t chCRCLTable[] = // CRC 低位字节值表
    {
        0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
        0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
        0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
        0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
        0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
        0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
        0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
        0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
        0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
        0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
        0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
        0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
        0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
        0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
        0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
        0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
        0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
        0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
        0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
        0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
        0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
        0x41, 0x81, 0x80, 0x40};

uint16_t CRC16_1(uint8_t *pchMsg, int16_t wDataLen)
{
    uint8_t chCRCHi = 0xFF; // 高CRC字节初始化
    uint8_t chCRCLo = 0xFF; // 低CRC字节初始化
    int16_t wIndex;         // CRC循环中的索引
    while (wDataLen--)
    {
        // 计算CRC
        wIndex = chCRCHi ^ *pchMsg++;
        chCRCHi = chCRCLo ^ chCRCHTable[wIndex];
        chCRCLo = chCRCLTable[wIndex];
    }
    return ((chCRCHi << 8) | chCRCLo);
}
#endif

union union_sys_savepara allrings_paras = // 万能环系统存储参数
    {

        .allsys_para.head_check = EEPROM_HEAD_CHECK_VALUES,
        .allsys_para.can_id = 1,
        .allsys_para.rings_devices = RINGS_RS485_EPG_XX, // RINGS_RS485_SRT_IM, // RINGS_NO_DEVICE,
        .allsys_para.upgrate_flag = BootFLag_JumpAPP,
};

static void allrings_defalut_para(union union_sys_savepara *defaults)
{
    defaults->allsys_para.head_check = EEPROM_HEAD_CHECK_VALUES;
    defaults->allsys_para.can_id = 1;
    defaults->allsys_para.rings_devices = RINGS_RS485_EPG_XX, // RINGS_RS485_SRT_IM; // RINGS_NO_DEVICE;
    defaults->allsys_para.canfd_broadcast_index = 7;
}

static uint8_t ee_buff[EEPROM_SAVED_PARA_SIZE];
static uint8_t allrings_sys_saved_check(uint8_t *data, uint16_t eeprom_saved_addr, uint16_t eeprom_backup_addr, uint16_t _saved_size)
{
#ifdef USE_CRC_CHECK
    static uint16_t newcrc, oldcrc;

    newcrc = CRC16_1(data, _saved_size - 2);
    data[_saved_size - 2] = (uint8_t)(newcrc >> 8);
    data[_saved_size - 1] = (uint8_t)(newcrc & 0x00ff);

    I2C_EE_RANDOM_Write(allrings_paras.data, eeprom_saved_addr, _saved_size);  // 默认参数写到存储区
    I2C_EE_RANDOM_Write(allrings_paras.data, eeprom_backup_addr, _saved_size); // 默认参数写到备份区

    I2C_EE_PageRead(ee_buff, eeprom_saved_addr, _saved_size); // 读取EE系统备份参数
    newcrc = CRC16_1(ee_buff, _saved_size - 2);
    oldcrc = ee_buff[_saved_size - 2] << 8 | ee_buff[_saved_size - 1];

    if (newcrc == oldcrc) // 备份区正常，则将备份区数据写到存储区
    {
        return 1;
    }
    else
        return 0;
#else
    I2C_EE_RANDOM_Write(allrings_paras.data, eeprom_saved_addr, _saved_size);  // 默认参数写到存储区
    I2C_EE_RANDOM_Write(allrings_paras.data, eeprom_backup_addr, _saved_size); // 默认参数写到备份区
#endif
    return 0;
}

/**/
#if 0
void user_get_para_eeprom(void)
{
    EE_IIC_Init();
    I2C_EE_PageRead(&allrings_paras.data[0], EEPROM_ADDR_SYS_PARA, EEPROM_SAVED_PARA_SIZE); // 读取EE系统参数
    for (uint8_t i; i < 10; i++)
        rt_kprintf("eeprom[%d]:%X \r\n", i, allrings_paras.data[i]);
}
#else
void user_get_para_eeprom(void)
{
#ifdef USE_CRC_CHECK
    uint16_t newcrc, oldcrc;
#endif
    EE_IIC_Init();

    I2C_EE_PageRead(&allrings_paras.data[0], EEPROM_ADDR_SYS_PARA, EEPROM_SAVED_PARA_SIZE); // 读取EE系统参数

    if (allrings_paras.allsys_para.head_check != EEPROM_HEAD_CHECK_VALUES) // 存储参数头部判断异常，说明没有写过数据
    {
        allrings_defalut_para(&allrings_paras);
        allrings_sys_saved_check(allrings_paras.data, EEPROM_ADDR_SYS_PARA, EEPROM_ADDR_SYS_PARA_BACK, EEPROM_SAVED_PARA_SIZE);
    }
    else // 有头部，则需要进行判断参数区和备份区的情况
    {
#ifdef USE_CRC_CHECK
        newcrc = CRC16_1(&allrings_paras.data[0], EEPROM_SAVED_PARA_SIZE - 2);
        oldcrc = allrings_paras.data[EEPROM_SAVED_PARA_SIZE - 2] << 8 | allrings_paras.data[EEPROM_SAVED_PARA_SIZE - 1];
        if (newcrc != oldcrc) // 如果查到数据不正确，查备份区情况
        {
            I2C_EE_PageRead(&allrings_paras.data[0], EEPROM_ADDR_SYS_PARA_BACK, sizeof(union union_sys_savepara)); // 读取EE系统备份参数
            newcrc = CRC16_1(&allrings_paras.data[0], EEPROM_SAVED_PARA_SIZE - 2);
            oldcrc = allrings_paras.data[EEPROM_SAVED_PARA_SIZE - 2] << 8 | allrings_paras.data[EEPROM_SAVED_PARA_SIZE - 1];

            if (newcrc == oldcrc) // 备份区正常，则将备份区数据写到存储区
            {
                I2C_EE_RANDOM_Write(allrings_paras.data, EEPROM_ADDR_SYS_PARA, sizeof(union union_sys_savepara));
            }
            else // 备份区也异常，直接上报eeprom错误
            {
                rings_ctr.allrings_values.allsys_err.bit.EEPROM = 1; // EEPROM异常
            }
        }
#endif
    }
}
#endif
/*写入升级标志位到EE*/

void allrings_sys_para_ctr(void)
{
    allrings_sys_saved_check(allrings_paras.data, EEPROM_ADDR_SYS_PARA, EEPROM_ADDR_SYS_PARA_BACK, EEPROM_SAVED_PARA_SIZE);

    if (allrings_paras.allsys_para.upgrate_flag == 0) // 进行BOOT升级操作
    {
        rt_hw_cpu_reset(); // 重启方式，跳入BOOT中
    }
}

/*线程函数*/
void tpara_save_main_entry(void *parameter)
{
    while (1)
    {
        rt_sem_take(sem_saved, RT_WAITING_FOREVER); // 一直等待信号量
        allrings_sys_para_ctr();
        rt_thread_mdelay(100);
    }
}

int sys_para_saved(void)
{
    sem_saved = rt_sem_create("all_save", 0, RT_IPC_FLAG_PRIO);
    /*	create  thread    */
    tpara_save = rt_thread_create("sys_para", tpara_save_main_entry, RT_NULL,
                                  THREAD_STACK_PARA_SAVE, THREAD_PRIO_PARA_SAVE, 50);

    if (tpara_save == RT_NULL)
    {
        rt_sem_delete(sem_saved);
        LOG_E("system para_saved thread create failed! \r\n");
        return RT_ERROR;
    }
    else
        rt_thread_startup(tpara_save); // 启动线程

    return RT_EOK;
}
INIT_APP_EXPORT(sys_para_saved);

uint8_t eeprom_test(void)
{
    I2C_EE_RANDOM_Write(allrings_paras.data, 0, 128);

    HAL_Delay(10);

    I2C_EE_PageRead(ee_buff, 0, 128);

    HAL_Delay(10);

    for (uint8_t i = 0; i < 128; i++)
    {
        if (allrings_paras.data[i] != ee_buff[i])
        {
            rt_kprintf("eeprom test failed!");
            return 0;
        }
    }
    rt_kprintf("eeprom test ok!");
    return 1;
}
// MSH_CMD_EXPORT(eeprom_test,timer sample used for eeprom test);

uint8_t eeprom_para_output(void)
{
    LOG_I("System eeprom information");
    LOG_I("canid = %d", allrings_paras.allsys_para.can_id);
    LOG_I("upgrate_flag   = %d", allrings_paras.allsys_para.upgrate_flag);
    LOG_I("device  = %d", allrings_paras.allsys_para.rings_devices);
    LOG_I("fdcan_broadcastindex  = %d", allrings_paras.allsys_para.canfd_broadcast_index);

    return RT_EOK;
}
MSH_CMD_EXPORT(eeprom_para_output, output system eeprom parameters);
/**************************sys参数操作接口函数**********************************/
